深入ES6 (四) Module中的export与import

前言

历史上,JavaScript 一直没有模块(module)体系,无法将一个大程序拆分成互相依赖的小文件,再用简单的方法拼装起来。其他语言都有这项功能,比如 Ruby 的require、Python 的import,甚至就连 CSS 都有@import,但是 JavaScript 任何这方面的支持都没有,这对开发大型的、复杂的项目形成了巨大障碍。

今天来聊聊ES6中比较重要且使用的东西,也就是Module(模块),终于在ES6时,Javascript也具有模块化的功能,ES6中的Module具备着AMD和CMD的双重特性,也就是说,从ES6时代,我们可以告别SeaJs和requireJs了。


正文从这里开始,首先明确一点,ES6中的Module自动采用严格模式 (“use strict”),也就是说,采用ES6的模块机制必须遵循js的严格模式。

js的严格模式是ES5就发明的,这里不多说严格模式有哪些限制了。


Module功能主要由两个命令构成:export和import。export命令用于规定模块的对外接口,import命令用于输入其他模块提供的功能。

export

在javascript中,我们习惯把一个js文件当做一个模块,当然通过export输出

1
2
export var a = "a";
export var year = 1998;

通常我们习惯这样使用export,把要输出的值放在一个对象集里。

1
2
3
4
5
var firstName = 'huang';
var lastName = 'zhaonan';
var year = 1958;
export {firstName, lastName, year};

export命令除了输出变量,还可以输出函数或类(class)。

1
2
3
export function aaa(){
return 123
}

通常情况下,export输出的变量就是本来的名字,但是可以使用as关键字重命名。

1
2
3
4
5
6
7
8
function v1() { ... }
function v2() { ... }
export {
v1 as streamV1,
v2 as streamV2,
v2 as streamLatestVersion
};

需要注意一点,export输出时,必须输出定义过的属性
1
2
3
4
5
6
// 报错
export 1;
// 报错
var m = 1;
export m;

输出函数时还需要注意一点,必须用对象集包裹函数

1
2
function f() {}
export {f};

export输出的内容并是不想象中静态的,实际上写个定时器,值还是会变得

1
2
export var time = 10;
setTimeout(() => time = 20,500)

import

使用export命令定义了模块的对外接口以后,其他 JS 文件就可以通过import命令加载这个模块。

1
2
3
4
5
6
// main.js
import {firstName, lastName, year} from './profile';
function setName(element) {
element.textContent = firstName + ' ' + lastName;
}

如果想为输入的变量重新取一个名字,import命令要使用as关键字,将输入的变量重命名。

1
import { lastName as surname } from './profile';

因为import是静态执行,所以import不支持运算

1
import {'a' + 'b'} from './profile'//报错

目前阶段,通过 Babel 转码,CommonJS 模块的require命令和 ES6 模块的import命令,可以写在同一个模块里面,但是最好不要这样做。因为import在静态解析阶段执行,所以它是一个模块之中最早执行的。下面的代码可能不会得到预期结果。

1
2
3
require('core-js/modules/es6.symbol');
require('core-js/modules/es6.promise');
import React from 'React';//最先执行

export default

从import来看,每一次引入时都需要知道前文件输出的名称是什么,为了给开发者的优秀体验,export default就是为了默认输出不命名出现的,
而引入时,可以随意起名字。

1
2
3
4
5
6
7
8
9
// export-default.js
export default function () {
console.log('foo');
}
// import-default.js
import customName from './export-default';
customName(); // 'foo'

export default命令其实只是输出一个叫做default的变量,所以它后面不能跟变量声明语句。

1
2
3
4
5
6
7
8
9
10
11
12
// modules.js
function add(x, y) {
return x * y;
}
export {add as default};
// 等同于
// export default add;
// app.js
import { default as xxx } from 'modules';
// 等同于
// import xxx from 'modules';

export default 只能被使用一次,第二次使用会报错

1
2
export default 'one'
export default 'two'//报错

import()

前文讲到过,因为import属于静态执行,无论写在哪里都会默认第一步执行。所以想达到像CMD一样的延时加载的效果,ES6也是有考虑到的,就要用到import()了


import()的一些使用场景
1、判断

1
2
3
4
5
if (condition) {
import('moduleA').then(...);
} else {
import('moduleB').then(...);
}

2、按需加载

1
2
3
4
5
6
7
8
9
button.addEventListener('click', event => {
import('./dialogBox.js')
.then(dialogBox => {
dialogBox.open();
})
.catch(error => {
/* Error handling */
})
});

3、动态的模块路径

1
import(f()).then(...);

Module 的浏览器加载

在 HTML 网页中,浏览器通过